#include "prop.h"

//************************************
//************Seleccion***************
//************************************
Expresion seleccionaExpresionNodo(Proposiciones proposicion){
    return proposicion->info;
}

char seleccionarNodoLetraProposicional(Proposiciones proposicion){
    return seleccionarLetraProposicional(seleccionaExpresionNodo(proposicion));
}

Tipo_Expresion seleccionarNodoTipoExpresion(Proposiciones proposicion){
    return seleccionarTipoExpresion(seleccionaExpresionNodo(proposicion));
}

Conectivas seleccionarNodoConectiva(Proposiciones proposicion){
    return seleccionarConectiva(seleccionaExpresionNodo(proposicion));
}


//************************************
//***********Comparacion**************
//************************************

bool esNodoLetraProposicional(Proposiciones proposicion){
    return esExpresionLetraProposicional(seleccionaExpresionNodo(proposicion));
}

bool esNodoConectiva(Proposiciones proposicion){
    return esExpresionConectiva(seleccionaExpresionNodo(proposicion));
}

//************************************
//*******Creacion y construccion******
//************************************

void crearProposicion(Proposiciones &proposicion) {
    proposicion = NULL;
}

void InsertaNodoClon(Proposiciones proposicion, Proposiciones &proposicionClon){
    proposicionClon=new Nodo;
    proposicionClon->info=proposicion->info;
    proposicionClon->hizq=NULL;
    proposicionClon->hder=NULL;
}

void clonaProposicion(Proposiciones proposicion, Proposiciones &proposicionClon){
    if (proposicion!=NULL){
        InsertaNodoClon(proposicion, proposicionClon);
        clonaProposicion(proposicion->hizq,proposicionClon->hizq);
        clonaProposicion(proposicion->hder,proposicionClon->hder);
    }
}

void agregarParentesisIzquierdo(Proposiciones &proposicion){
    if (proposicion==NULL){
        proposicion=new Nodo;
        cargarExpresionParentesis(cargarParentesisIzquierdo(),proposicion->info);
        proposicion->hizq=NULL;
        proposicion->hder=NULL;
    }else
        agregarParentesisIzquierdo(proposicion->hizq);
}

void agregarParentesisDerecho(Proposiciones &proposicion){
    if (proposicion==NULL){
        proposicion=new Nodo;
        cargarExpresionParentesis(cargarParentesisDerecho(),proposicion->info);
        proposicion->hizq=NULL;
        proposicion->hder=NULL;
    }else
        agregarParentesisDerecho(proposicion->hder);
}


Proposiciones constructorDeProposicion(Proposiciones proposicionIzquierda, Expresion expresion, Proposiciones proposicionDerecha){

Proposiciones proposicion,proposicionClonIzquierda,proposicionClonDerecha;

    crearProposicion(proposicion);

    crearProposicion(proposicionClonIzquierda);     //Creo copias de las proposiciones para que sean independientes de las originales
    crearProposicion(proposicionClonDerecha);
    clonaProposicion(proposicionIzquierda,proposicionClonIzquierda);
    clonaProposicion(proposicionDerecha,proposicionClonDerecha);

    proposicion=new Nodo;                           //Creo un nodo con la expresion pasada como parametro para formar el nuevo arbol
    proposicion->info=expresion;
    proposicion->hizq=proposicionClonIzquierda;     //apunta a la memoria donde esta el hijo izquierdo
    proposicion->hder=proposicionClonDerecha;       //apunta a la memoria donde esta el hijo derecho

    if(proposicionDerecha!=NULL){                  //A los hijos les pongo los parentesis
        agregarParentesisIzquierdo(proposicion);
        agregarParentesisDerecho(proposicion);
    }

    return proposicion;
}

//************************************
//**********Eliminacion***************
//************************************

void borrarProposicion(Proposiciones &proposicion){
    if (proposicion!=NULL){
        if(proposicion->hizq==NULL&&proposicion->hder==NULL)
            delete proposicion;
        else{
            if(proposicion->hizq!=NULL)
                borrarProposicion(proposicion->hizq);
            if(proposicion->hder!=NULL)
                borrarProposicion(proposicion->hder);
        }
    }
}

//************************************
//************Despliegue**************
//************************************

void desplegarProposicion(Proposiciones proposicion){
    if (proposicion!=NULL){
        desplegarProposicion(proposicion->hizq);
        desplegarExpresion(proposicion->info);
        desplegarProposicion(proposicion->hder);
    }
}

//************************************
//************Consultas***************
//************************************

void atomosEnLaProposicion(Proposiciones proposicion,Atoms &atoms){

    if (proposicion!=NULL){
        atomosEnLaProposicion(proposicion->hizq,atoms);
        if(esNodoLetraProposicional(proposicion))
            if(!existeAtom(atoms,seleccionarNodoLetraProposicional(proposicion)))
                insertaAtoms(atoms,seleccionarNodoLetraProposicional(proposicion));
        atomosEnLaProposicion(proposicion->hder,atoms);
    }
}


Tipo_Evaluacion eval(Proposiciones proposicion){
Atoms atoms;
int i=0,cantidadRecorridas;
bool recorridaActual,recorridaAnterior,esContingencia=false;

    crearAtoms(atoms);
    atomosEnLaProposicion(proposicion,atoms);                       //creo atoms para tener las letras de la prop ordenadas (este orden no sera alterado)
    cantidadRecorridas=potenciaAtoms(cuentaAtoms(atoms));           //calculo la cantidad de reccorridas o filas de la tabla de verdad

    recorridaAnterior=evaluaRecorrida(proposicion,atoms,i);         //evaluo la primer recorrida obteniendo su valor de verdad
    i++;
    while(i<cantidadRecorridas&&!esContingencia){                   //se realizaran recorridas para toda la "tabla de verdad" o hasta que encuentre una contingencia
        recorridaActual=evaluaRecorrida(proposicion,atoms,i);       //Evaluo la siguiente recorrida
        if(recorridaAnterior!=recorridaActual){                     //Si es diferente a la anterior es contingencia y dejo de comparar
            esContingencia=true;
        }
        i++;
    }
    borrarAtoms(atoms);                                             //borro la lista de atomos pues ya no la utilizare

    if(esContingencia)                                              //Retorno la evaluacion
        return contingencia;
    else{
        if(recorridaActual)
            return tautologia;
        else
            return contradiccion;
    }
}


bool evaluaRecorrida(Proposiciones proposicion,Atoms atoms,int i){  //evalua el valor de verdad para una recorrida de la "tabla de verdad"

    if(esNodoLetraProposicional(proposicion))         //Si es una letra proposicional retorno su valor binario para la determinada recorrida

        return convierteEnteroABinario(i,posicionDelAtomo(atoms,seleccionarNodoLetraProposicional(proposicion)),cuentaAtoms(atoms));

    else{       //Si es una conectiva, dependiendo de cual sea el valor que retornara

        if(esNodoConectiva(proposicion)){

            switch(seleccionarNodoConectiva(proposicion)){

                case Not:
                    if(evaluaRecorrida(proposicion->hder,atoms,i))                      //Si es la conectiva Not, invierte la evaluacion de su hijo derecho
                        return false;
                    else
                        return true;
                    break;

                case And:
                    if(evaluaRecorrida(proposicion->hizq,atoms,i)&&evaluaRecorrida(proposicion->hder,atoms,i))  //Si es un And retorna true solo si ambos hijos son true
                        return true;
                    else
                        return false;
                    break;

                case Or:
                    if(evaluaRecorrida(proposicion->hizq,atoms,i)||evaluaRecorrida(proposicion->hder,atoms,i))  //Si es un Or retorna true si almenos uno de sus hijos es true
                        return true;
                    else
                        return false;
                    break;

                case entonces:
                    if(evaluaRecorrida(proposicion->hizq,atoms,i)&&!evaluaRecorrida(proposicion->hder,atoms,i)) //Si es un entonces retorna false solo si el hijo hizquierdo es verdadero y el derecho falso
                        return false;
                    else
                        return true;
                    break;

                case siysolosi:
                    if(evaluaRecorrida(proposicion->hizq,atoms,i)==evaluaRecorrida(proposicion->hder,atoms,i))  //Si es un Si solo Si retorna true solo si ambos hijos tienen el mismo valor de verdad
                        return true;
                    else
                        return false;
                    break;

                default:;
            }
        }
    }
}


bool convierteEnteroABinario(int recorrida, int pos,int cantidadLetras){
int binario,cant=cantidadLetras,i=1;
    if(recorrida==0)                      //en la recorrida 0 de una "tabla de verdad" todas las letras proposicionales tienen valor de verdad 0, por tanto retorno false
        return false;
    else{
        if(recorrida==1){                 //en la recorrida 1 de una "tabla de verdad" todas las letras proposicionales tienen valor de verdad 0, menos la ultima letra proposicional
            cantidadLetras--;
            if(pos==cantidadLetras)
                return true;
            else
                return false;
        }else{                      //Para las otras recorridas calculo el valor binario para las letras proposicionales
            if(pos<(cant-cantidadBits(recorrida)))    //si estoy buscando el valor binario de posiciones que estan a la izquierda en la "tabla", los mismos son siempre relleno de ceros
                return 0;
            else{
                while (recorrida>0&&(cantidadLetras-pos)>=i){ // Calculo el valor binario para la posicion
                    binario=recorrida % 2;
                    recorrida = (recorrida-binario)/ 2;
                    i++;
                }
                return binario;
            }
        }
    }
}

int cantidadBits(int num){      //Es la cantidad de bits que se necesitan para representar el numero pasado por parametro
int i=0;
    while(num>0){
        num=num/2;
        i++;
    }
    return i;
}

